"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const git_utils_1 = require("../utils/git-utils");
const child_process = __importStar(require("child_process"));
const fs = require('fs-extra');
const bootstrap = () => __awaiter(void 0, void 0, void 0, function* () {
    const path = require('path');
    process.env.BABEL_ENV = 'production';
    process.env.NODE_ENV = 'production';
    const { envIsChanged, envData } = require('../config/env'); // 环境变量是否变化，获取所有环境变量数据
    const paths = require('../config/paths');
    const { setCachedEnv } = require('../utils/cache'); // 写入缓存环境变量数据
    const compileEnvFile = path.resolve(paths.appConfig, 'compileBuildEnv.js');
    if (fs.existsSync(compileEnvFile)) {
        Object.assign(process.env, require(compileEnvFile));
    }
    // process.env.BABEL_ENV = 'development';
    // process.env.NODE_ENV = 'development';
    // Makes the script crash on unhandled rejections instead of silently
    // ignoring them. In the future, promise rejections that are not handled will
    // terminate the Node.js process with a non-zero exit code.
    process.on('unhandledRejection', (err) => {
        throw err;
    });
    // process.on('SIGINT', () => {
    //   process.exit(0);
    // });
    // Ensure environment variables are read.
    const chalk = require('react-dev-utils/chalk');
    const webpack = require('webpack');
    const bfj = require('bfj');
    const configFactory = require('../config/webpack.ms.config');
    const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
    const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
    const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
    const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
    const measureFileSizesBeforeBuild = FileSizeReporter.measureFileSizesBeforeBuild;
    const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
    const useYarn = fs.existsSync(paths.yarnLockFile);
    // These sizes are pretty large. We'll warn for bundles exceeding them.
    const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
    const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
    const SKIP_NO_CHANGE_MODULE = process.env.SKIP_NO_CHANGE_MODULE === 'true';
    const DISABLE_BUILD_DLL = process.env.DISABLE_BUILD_DLL === 'true';
    const isInteractive = process.stdout.isTTY;
    // Warn and crash if required files are missing
    if (!checkRequiredFiles([paths.appHtml])) {
        console.log(paths.appHtml);
        process.exit(1);
    }
    // // Process CLI arguments
    // const argv = process.argv.slice(2);
    // const writeStatsJson = argv.indexOf('--stats') !== -1;
    const BUILD_MODULE = process.env.BUILD_MODULE; // 传入的子模块 name
    if (typeof BUILD_MODULE !== 'string') {
        console.error('error: 请指定编译模块名的环境变量 BUILD_MODULE   \nexport BUILD_MODULE=xxx-module');
        process.exit(1);
    }
    const appPackage = require(paths.appPackageJson);
    const name = appPackage.name; // package.json 里的 name
    const microModueInfoFile = path.resolve(paths.appRootBuild, `packages/${appPackage.name}/package.json`); // 获取子模块下 的 package.json
    if (SKIP_NO_CHANGE_MODULE) {
        // 跳过未改变的 子模块 不打包
        if (fs.existsSync(microModueInfoFile)) {
            // 子模块 下存在  package.json
            try {
                const lastPackageMicroInfo = require(microModueInfoFile);
                let packageFilesIsNotChange = false;
                if (lastPackageMicroInfo.commitHash) {
                    packageFilesIsNotChange = !(yield git_utils_1.fileHasChangeFromCommit(`packages/${name}`, lastPackageMicroInfo.commitHash));
                }
                else if (lastPackageMicroInfo.version) {
                    packageFilesIsNotChange =
                        lastPackageMicroInfo.version === appPackage.version;
                }
                const _envIsChanged = yield envIsChanged();
                if (_envIsChanged) {
                    throw new Error('环境变量变化！');
                }
                if (packageFilesIsNotChange) {
                    console.log(`${appPackage.name} 模块文件没有变化，跳过编译`);
                    return;
                }
            }
            catch (_a) { }
        }
    }
    console.log(`正在编译 ${appPackage.name} 模块`);
    const parentDistIndexHtmlPath = path.join(paths.appRootBuild, `index.html`);
    // We require that you explicitly set browsers and do not fall back to
    // browserslist defaults.
    const { checkBrowsers } = require('react-dev-utils/browsersHelper');
    // Create the production build and print the deployment instructions.
    function build(previousFileSizes) {
        return __awaiter(this, void 0, void 0, function* () {
            console.log('Creating an optimized production build...');
            // Generate configuration
            const config = configFactory('production');
            let compiler = webpack(config);
            return new Promise((resolve, reject) => {
                compiler.run((err, stats) => {
                    let messages;
                    if (err) {
                        if (!err.message) {
                            return reject(err);
                        }
                        messages = formatWebpackMessages({
                            errors: [err.message],
                            warnings: [],
                        });
                    }
                    else {
                        messages = formatWebpackMessages(stats.toJson({
                            all: false,
                            warnings: true,
                            errors: true,
                        }));
                    }
                    if (messages.errors.length) {
                        // Only keep the first error. Others are often indicative
                        // of the same problem, but confuse the reader with noise.
                        if (messages.errors.length > 1) {
                            messages.errors.length = 1;
                        }
                        return reject(new Error(messages.errors.join('\n\n')));
                    }
                    if (process.env.CI &&
                        (typeof process.env.CI !== 'string' ||
                            process.env.CI.toLowerCase() !== 'false') &&
                        messages.warnings.length) {
                        console.log(chalk.yellow('\nTreating warnings as errors because process.env.CI = true.\n' +
                            'Most CI servers set it automatically.\n'));
                        console.warn(messages.warnings.join('\n\n'));
                        // return reject(new Error(messages.warnings.join('\n\n')));
                    }
                    const resolveArgs = {
                        stats,
                        previousFileSizes,
                        warnings: messages.warnings,
                        config,
                    };
                    const assetsChunkData = stats.toJson().assetsByChunkName; // 得到 编译后的 主文件
                    return bfj
                        .write(paths.appRootBuild +
                        `/packages/${appPackage.name}/bundle-stats.json`, assetsChunkData)
                        .then(() => {
                        return updateParentMicroConfig(assetsChunkData);
                    }) // 先 写入主文件 地址  然后更新 父模块microConfig.json
                        .then(() => resolve(resolveArgs))
                        .catch((error) => reject(new Error(error)));
                });
            });
        });
    }
    /**
     * 更新汇总文件
     * @param assetsChunkData
     */
    function updateParentMicroConfig(assetsChunkData) {
        return __awaiter(this, void 0, void 0, function* () {
            const cpaasConfig = paths.getCpaasConfig(paths.appPath) || {};
            let indexAssets = typeof assetsChunkData.main === 'string'
                ? [assetsChunkData.main]
                : assetsChunkData.main;
            if (assetsChunkData['runtime~main']) {
                if (typeof assetsChunkData['runtime~main'] === 'string') {
                    indexAssets.unshift(assetsChunkData['runtime~main']);
                }
                else {
                    indexAssets = assetsChunkData['runtime~main'].concat(indexAssets);
                }
            }
            let IS_PUBLIC_MS = process.env.BUILD_PUBLIC_MS === 'true';
            const cpaasRootConfig = paths.getCpaasBootConfig(paths.appPath) || {};
            let cpaasBootPackageConfig = cpaasRootConfig.packages.find((item) => item.name === appPackage.name) ||
                {};
            const microPackageConfig = Object.assign(Object.assign(Object.assign({}, cpaasBootPackageConfig), cpaasConfig.package), { name: appPackage.name, indexAssets });
            if (!IS_PUBLIC_MS && microPackageConfig.public) {
                IS_PUBLIC_MS = true;
            }
            if (IS_PUBLIC_MS && !microPackageConfig.public) {
                microPackageConfig.public = true;
            }
            const microConfigData = {
                [appPackage.name]: microPackageConfig,
            };
            let oldMicroMapConfig;
            let commitHash;
            try {
                commitHash = yield git_utils_1.getHeadId();
            }
            catch (_a) { }
            yield bfj.write(microModueInfoFile, Object.assign({ commitHash, version: appPackage.version }, microPackageConfig));
            let microMapConfigPath = path.resolve(paths.appRootBuild, 'packages/microConfig.json');
            if (fs.existsSync(microMapConfigPath)) {
                oldMicroMapConfig = fs.readJSONSync(microMapConfigPath);
            }
            else {
            }
            let resultMicroConfigData = Object.assign(Object.assign({}, oldMicroMapConfig), microConfigData);
            fs.writeFileSync(microMapConfigPath, JSON.stringify(resultMicroConfigData));
            if (fs.existsSync(paths.appMsPublic)) {
                fs.copySync(paths.appMsPublic, path.resolve(paths.appRootBuild, `packages/${appPackage.name}`), {
                    dereference: true,
                    filter: (file) => file !== paths.appHtml,
                });
            }
            const isPublicMs = IS_PUBLIC_MS || !!microPackageConfig.public;
            if (isPublicMs) {
                fs.writeFileSync(path.resolve(paths.appRootBuild, `packages/${appPackage.name}/package.js`), `
        (function(e, n) {
          "object" === typeof exports && "object" === typeof module ? module.exports = n() : "function" === typeof define && define.amd ? define([], n) : "object" === typeof exports ? exports["cpaasJsonp_packages/${appPackage.name}-package"](n()) : e["cpaasJsonp_packages/${appPackage.name}-package"](n())
        })(window, function() {
          return ${JSON.stringify(microPackageConfig)};
        });
      `);
            }
            const microMapConfigPathJsonp = path.resolve(paths.appRootBuild, 'packages/microConfig.js');
            const publicPackagesConfigData = {};
            Object.keys(resultMicroConfigData).forEach((name) => {
                const packageConfig = resultMicroConfigData[name];
                if (packageConfig.public) {
                    publicPackagesConfigData[name] = packageConfig;
                }
            });
            fs.writeFileSync(microMapConfigPathJsonp, `
      (function(e, n) {
        "object" === typeof exports && "object" === typeof module ? module.exports = n() : "function" === typeof define && define.amd ? define([], n) : "object" === typeof exports ? exports["cpaasJsonp_packages"](n()) : e["cpaasJsonp_packages"](n())
      })(window, function() {
        return ${JSON.stringify(publicPackagesConfigData)};
      });
    `);
        });
    }
    try {
        yield checkBrowsers(paths.appPath, isInteractive);
        if (!DISABLE_BUILD_DLL) {
            if (!fs.existsSync(paths.appDll)) {
                yield require('./build-dll').default();
            }
        }
        const buildParent = () => __awaiter(void 0, void 0, void 0, function* () {
            try {
                yield new Promise((resolve, reject) => {
                    const args = [
                        path.resolve(__dirname, './build-only-build-parent.bin'),
                    ];
                    const p = child_process.spawn('node', args, {
                        env: Object.assign(Object.assign({}, process.env), { BUILD_MODULE: '' }),
                        cwd: paths.appRootPath,
                        detached: false,
                        windowsHide: true,
                    });
                    p.stderr.pipe(process.stderr);
                    p.stdout.pipe(process.stdout);
                    p.on('close', (code) => {
                        p.kill();
                        if (code === 0) {
                            resolve(null);
                        }
                        else {
                            reject(code);
                        }
                    });
                });
            }
            catch (e) {
                console.error(e);
                process.exit(1);
            }
        });
        if (process.env.BUILD_SKIP_PARENT !== 'true' &&
            !fs.existsSync(parentDistIndexHtmlPath)) {
            console.log('运行子模块微前端编译之前, 需要编译父模块, 正在编译父模块。。。 ');
            yield buildParent();
        }
        let previousFileSizes = null;
        try {
            previousFileSizes = yield measureFileSizesBeforeBuild(paths.appRootBuild);
        }
        catch (e) {
            console.error(e);
        }
        const parentSubDistDir = path.resolve(paths.appRootBuild, `packages/${appPackage.name}`);
        // console.log(parentSubDistDir);
        try {
            fs.emptyDirSync(parentSubDistDir); // 清空dist 下 packages的 子模块 内容
        }
        catch (e) {
            console.error(e);
        }
        const { stats, warnings, config } = (yield build(previousFileSizes));
        if (!stats) {
            return;
        }
        if (warnings.length) {
            console.log(chalk.yellow('Compiled with warnings.\n'));
            console.log(warnings.join('\n\n'));
            console.log('\nSearch for the ' +
                chalk.underline(chalk.yellow('keywords')) +
                ' to learn more about each warning.');
            console.log('To ignore, add ' +
                chalk.cyan('// eslint-disable-next-line') +
                ' to the line before.\n');
        }
        else {
            console.log(chalk.green('Compiled successfully.\n'));
        }
        yield setCachedEnv(envData);
        console.log('File sizes after gzip:\n');
        // console.log(
        //   stats,
        //   previousFileSizes,
        //   paths.appRootBuild,
        // );
        // process.exit();
        if (previousFileSizes) {
            try {
                printFileSizesAfterBuild(stats, previousFileSizes, paths.appRootBuild, WARN_AFTER_BUNDLE_GZIP_SIZE, WARN_AFTER_CHUNK_GZIP_SIZE);
            }
            catch (e) {
                console.error(e);
            }
        }
        console.log();
        const publicUrl = paths.publicUrl;
        const publicPath = config.output.publicPath;
        const buildFolder = path.relative(process.cwd(), paths.appRootBuild);
        printHostingInstructions(appPackage, publicUrl, publicPath, buildFolder, useYarn);
    }
    catch (errMsg) {
        console.log(chalk.red('Failed to compile.\n'));
        // printBuildError(errMsg);
        console.error(errMsg);
        process.exit(1);
    }
    console.log(`模块 ${appPackage.name} 编译完毕`);
});
exports.default = bootstrap;
